#include "common.h"
#include "tcpci_event.h"
#include "tcpci.h"
#include "tcpci_core.h"
#include "tcpci_timer.h"
#include "tcpm.h"

#undef LOG_TAG 
#define LOG_TAG    "tcpci_alert"
#include "elog.h"

#if CHIP_TYPE == CHIP_SG41620
extern int sgmdev_i2c_write16(uint8_t reg, uint16_t data);
#endif

struct tcpci_alert_handler {
    uint32_t bit_mask;
    int (*handler)(void);
};

#define DECL_TCPCI_ALERT_HANDLER(xbit, xhandler) {\
    .bit_mask = 1 << xbit,\
    .handler = xhandler, \
}

static int tcpci_alert_vendor_defined(void)
{
    // TODO
    return 0;
}

static int tcpci_alert_tx_failed(void)
{
    uint8_t tx_state;
    struct sgm41620_tcpc *dev = &g_tcpc_dev;

    PE_DBG("alert tx failed\n");
    tx_state = dev->pd_transmit_state;
    dev->pd_transmit_state = PD_TX_STATE_NO_GOOD_CRC;

    if (tx_state == PD_TX_STATE_WAIT_CRC_VDM)
        vdm_put_hw_event(dev, PD_HW_TX_FAILED);
    else
        pd_put_hw_event(dev, PD_HW_TX_FAILED);

    return 0; 
}

static int tcpci_alert_tx_discard(void)
{
	uint8_t tx_state = PD_TX_STATE_GOOD_CRC;
	bool retry_crc_discard = !!(p_tcpc_dev->tcpc_flags & TCPC_FLAGS_RETRY_CRC_DISCARD);

	tx_state = p_tcpc_dev->pd_transmit_state;
	p_tcpc_dev->pd_transmit_state = PD_TX_STATE_DISCARD;

	if (tx_state == PD_TX_STATE_WAIT_CRC_VDM)
        vdm_put_hw_event(p_tcpc_dev, PD_HW_TX_DISCARD);
	else {
		if (retry_crc_discard) {
#if CONFIG_USB_PD_RETRY_CRC_DISCARD
			p_tcpc_dev->pd_discard_pending = true;
			tcpc_enable_timer(p_tcpc_dev, PD_TIMER_DISCARD);
#else
			log_i("RETRY_CRC_DISCARD\n");
#endif	/* CONFIG_USB_PD_RETRY_CRC_DISCARD */
		} else {
			pd_put_hw_event(p_tcpc_dev, PD_HW_TX_FAILED);
		}
	}

    return 0;
}

static int tcpci_alert_tx_success()
{
    uint8_t tx_state;
    struct sgm41620_tcpc *dev = &g_tcpc_dev;

	struct pd_event evt = {
		.event_type = PD_EVT_CTRL_MSG,
		.msg = PD_CTRL_GOOD_CRC,
		.pd_msg = NULL,
	};

	tx_state = dev->pd_transmit_state;
	dev->pd_transmit_state = PD_TX_STATE_GOOD_CRC;	

	if (tx_state == PD_TX_STATE_WAIT_CRC_VDM)
		pd_put_vdm_event(dev, &evt,false);
	else {
		pd_put_event(dev, &evt, false);
	}

    return 0;
}

static int tcpci_alert_recv_msg(void)
{
    struct pd_msg *pd_msg = NULL;
    int retval = 0;

	pd_msg = __pd_alloc_msg(p_tcpc_dev);
	if (pd_msg == NULL) {
		tcpci_alert_status_clear(p_tcpc_dev, TCPC_REG_ALERT_RX_MASK);
		retval = -1;
		goto out;
	}

	retval = tcpci_get_message(p_tcpc_dev, pd_msg->payload, &pd_msg->msg_hdr,&pd_msg->frame_type);
	if (retval < 0 || pd_msg->msg_hdr == 0) {
		log_e("recv_msg failed: %d\n", rv);
		__pd_free_msg(p_tcpc_dev, pd_msg);
		goto out;
	}

	pd_put_pd_msg_event(p_tcpc_dev, pd_msg);
out:
	return retval;
}

static int tcpci_alert_recv_hard_reset()
{
    log_i("%s\n",__func__);
    struct sgm41620_tcpc *dev = &g_tcpc_dev;
    dev->pd_transmit_state = PD_TX_STATE_HARD_RESET;
    pd_put_hw_event(dev, PD_HW_RECV_HARD_RESET);
	
	//wake_up(&tcpc->tx_wait_que);
	pd_put_recv_hard_reset_event(dev);
	//? to do
	return tcpci_init_alert_mask(dev);
	
    //return 0;
}

static int tcpci_alert_rx_overflow()
{
    int rv;
	uint32_t alert_status;

    rv = tcpci_get_alert_status(p_tcpc_dev, &alert_status);
	if (rv)
		return rv;
    log_i("%s alert_status = 0x%x\n",__func__,alert_status);
	if (alert_status & TCPC_REG_ALERT_RX_STATUS)
		return tcpci_alert_recv_msg();
    
    return 0;
}
 int tcpci_alert_fault()
{
    uint8_t status = 0;

    tcpci_get_fault_status(p_tcpc_dev, &status);
    log_e("%s,FaultAlert=0x%x\r\n", __func__,status);
    tcpci_fault_status_clear(p_tcpc_dev, status);
    
    return 0;
}

static int tcpci_alert_cc_changed(void)
{
    return tcpc_typec_handle_cc_change(p_tcpc_dev);
}

static int tcpci_alert_power_status_changed()
{
	int rv = 0;
	uint16_t power_status = 0;

	// to do ops fail
	//rv = tcpci_get_power_status(p_tcpc_dev,&power_status);
    rv = tcpci_get_power_status(p_tcpc_dev,&power_status);
	if (rv < 0)
		return rv;

	if (p_tcpc_dev->tcpc_flags & TCPC_FLAGS_ALERT_V10)
		rv = tcpci_vbus_level_changed(p_tcpc_dev);

	return rv;

}

int tcpci_alert_wakeup()
{
	if (p_tcpc_dev->typec_lpm) {
		// tcpci_set_low_power_mode(p_tcpc_dev, false);
		tcpc_enable_lpm_timer(p_tcpc_dev, true);
	}

	return 0;
}

static const struct tcpci_alert_handler tcpci_alert_handlers[] = {
	DECL_TCPCI_ALERT_HANDLER(15, tcpci_alert_vendor_defined),
	DECL_TCPCI_ALERT_HANDLER(4, tcpci_alert_tx_failed),
	DECL_TCPCI_ALERT_HANDLER(5, tcpci_alert_tx_discard),
	DECL_TCPCI_ALERT_HANDLER(6, tcpci_alert_tx_success),
	DECL_TCPCI_ALERT_HANDLER(2, tcpci_alert_recv_msg),
	DECL_TCPCI_ALERT_HANDLER(7, NULL),
	DECL_TCPCI_ALERT_HANDLER(8, NULL),
	DECL_TCPCI_ALERT_HANDLER(3, tcpci_alert_recv_hard_reset),
	DECL_TCPCI_ALERT_HANDLER(10, tcpci_alert_rx_overflow),
	DECL_TCPCI_ALERT_HANDLER(9, tcpci_alert_fault),
	DECL_TCPCI_ALERT_HANDLER(0, tcpci_alert_cc_changed),
	DECL_TCPCI_ALERT_HANDLER(1, tcpci_alert_power_status_changed),
	DECL_TCPCI_ALERT_HANDLER(16, tcpci_alert_wakeup),
};

static inline bool tcpci_check_hard_reset_complete(
	struct sgm41620_tcpc *tcpc, uint32_t *alert_status)
{
	if ((*alert_status & TCPC_REG_ALERT_HRESET_SUCCESS)
			== TCPC_REG_ALERT_HRESET_SUCCESS) {
		*alert_status &= ~TCPC_REG_ALERT_HRESET_SUCCESS;
		pd_put_sent_hard_reset_event(tcpc);
		return true;
	}

	if (*alert_status & TCPC_REG_ALERT_TX_DISCARDED) {
		*alert_status &= ~TCPC_REG_ALERT_TX_DISCARDED;
		log_e("HResetFailed\n");
		pd_put_hw_event(tcpc, PD_HW_TX_FAILED);
		return true;
	}

	return false;
}


int tcpci_alert(void)
{
    int rv, i;
    uint32_t alert_status = 0, alert_mask = 0;
    const uint8_t typec_role = p_tcpc_dev->typec_role;
    const uint8_t vbus_level = p_tcpc_dev->vbus_level;

    rv = tcpci_get_alert_status(p_tcpc_dev, &alert_status);
    if (rv)
        return rv;

    rv = tcpci_get_alert_mask(p_tcpc_dev, &alert_mask);
    if (rv)
        return rv;

    alert_status &= alert_mask;
    tcpci_alert_status_clear(p_tcpc_dev, alert_status & (~TCPC_REG_ALERT_RX_MASK));

    if (typec_role == TYPEC_ROLE_UNKNOWN || typec_role >= TYPEC_ROLE_NR) {
        log_w("Wrong TypeC-Role: %d\n", typec_role);
        return tcpci_alert_status_clear(p_tcpc_dev, alert_status);
    }

    if ((p_tcpc_dev->tcpc_flags & TCPC_FLAGS_ALERT_V10) &&
        (alert_status & TCPC_REG_ALERT_EXT_VBUS_80))
        alert_status |= TCPC_REG_ALERT_POWER_STATUS;

#if CONFIG_USB_POWER_DELIVERY
    if (p_tcpc_dev->pd_transmit_state == PD_TX_STATE_WAIT_HARD_RESET) {
        //log_i("PD_TX_STATE_WAIT_HARD_RESET\r\n");
        tcpci_check_hard_reset_complete(p_tcpc_dev,&alert_status);
//        alert_status &= ~TCPC_REG_ALERT_TX_MASK;
    }
#endif	/* CONFIG_USB_POWER_DELIVERY */

    for (i = 0; i < ARRAY_SIZE(tcpci_alert_handlers); i++) {
        if (tcpci_alert_handlers[i].bit_mask & alert_status) {
            if (tcpci_alert_handlers[i].handler != 0)
                tcpci_alert_handlers[i].handler();
        }
    }

#if CONFIG_USB_POWER_DELIVERY
    if (alert_status & TCPC_REG_ALERT_RX_STATUS) {
        tcpc_event_thread_wake_up(p_tcpc_dev);
    }
#endif

//    if (!(alert_status & TCPC_REG_ALERT_RX_HARD_RST)) {
//        rv = tcpci_set_alert_mask(p_tcpc_dev, alert_mask);
//    }

    if (p_tcpc_dev->tcpc_flags & TCPC_FLAGS_ALERT_V10){
        return rv;
    }

    // should not be reached
    tcpci_vbus_level_refresh(p_tcpc_dev);
    if (vbus_level != p_tcpc_dev->vbus_level)
        rv = tcpci_vbus_level_changed(p_tcpc_dev);

    return 0;
}

static inline int tcpci_alert_vsafe0v(struct sgm41620_tcpc *tcpc)
{
	tcpc_typec_handle_vsafe0v(tcpc);

//#if CONFIG_USB_PD_SAFE0V_DELAY // for pd.port.src.09
#if 0
	//tcpc_enable_timer(tcpc, PD_TIMER_VSAFE0V_DELAY);
#else
	pd_put_vbus_safe0v_event(tcpc, true);
#endif	/* CONFIG_USB_PD_SAFE0V_DELAY */
	return 0;
}

static inline void tcpci_vbus_level_init_v10(struct sgm41620_tcpc *tcpc, uint16_t power_status)
{
	tcpc->vbus_level = power_status & TCPC_REG_POWER_STATUS_VBUS_PRES ?
			   TCPC_VBUS_VALID : TCPC_VBUS_INVALID;
	//log_i("%s : power_status = 0x%x,vbus_level = %d\r\n",__func__,power_status,tcpc->vbus_level);
	if (power_status & TCPC_REG_POWER_STATUS_EXT_VSAFE0V) {
		if (tcpc->vbus_level == TCPC_VBUS_INVALID){
			tcpc->vbus_level = TCPC_VBUS_SAFE0V;
			//log_i("TCPC_VBUS_SAFE0V\r\n");
		}	
		else
			log_w("ps_confused: 0x%04x\n", power_status);
	}
}

static void __tcpci_vbus_level_refresh(struct sgm41620_tcpc *tcpc)
{
	tcpc->vbus_level = tcpc->vbus_present ? TCPC_VBUS_VALID :
						TCPC_VBUS_INVALID;
	if (tcpc->vbus_safe0v) {
		if (tcpc->vbus_level == TCPC_VBUS_INVALID)
			tcpc->vbus_level = TCPC_VBUS_SAFE0V;
		else
			log_w("ps_confused: %d\n", tcpc->vbus_level);
	}
}

void tcpci_vbus_level_refresh(struct sgm41620_tcpc *tcpc)
{
	__tcpci_vbus_level_refresh(tcpc);

}

void tcpci_vbus_level_init(struct sgm41620_tcpc *tcpc, uint16_t power_status)
{
	if (tcpc->tcpc_flags & TCPC_FLAGS_ALERT_V10) {
		//log_i("tcpci_vbus_level_init: power=0x%x\n", power_status);
		tcpci_vbus_level_init_v10(tcpc, power_status);
		return;
	}

	tcpc->vbus_present = !!(power_status & TCPC_REG_POWER_STATUS_VBUS_PRES);
	__tcpci_vbus_level_refresh(tcpc);

}

int tcpci_vbus_level_changed(struct sgm41620_tcpc *tcpc)
{
	int rv = 0;

	//log_i("ps_change=%d\n", tcpc->vbus_level);

	rv = tcpc_typec_handle_ps_change(tcpc, tcpc->vbus_level);
	if (rv < 0){
		log_e("tcpci_vbus_level_changed rv < 0\r\n");
		return rv;
	}

/*#if CONFIG_USB_PD_SAFE5V_DELAY
	tcpc_enable_timer(tcpc, PD_TIMER_VSAFE5V_DELAY);
#else*/
	pd_put_vbus_changed_event(p_tcpc_dev);
//#endif /* CONFIG_USB_PD_SAFE5V_DELAY */

	if (tcpc->vbus_level == TCPC_VBUS_SAFE0V){
		//log_i("tcpci_alert_vsafe0v\r\n");
		rv = tcpci_alert_vsafe0v(tcpc);
	}

	return rv;
}

/*
 * [BLOCK] TYPEC power control changed
 */

static inline int tcpci_report_power_control_on(struct sgm41620_tcpc *tcpc)
{
	log_i("tcpci_report_power_control_off \n");
	tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_DISCHARGE);
	//tcpci_enable_auto_discharge(tcpc, true);
	tcpci_enable_force_discharge(tcpc, false, 0);

	return 0;
}

static inline int tcpci_report_power_control_off(struct sgm41620_tcpc *tcpc)
{
	log_i("tcpci_report_power_control_off \n");
	tcpci_enable_force_discharge(tcpc, true, 0);
	tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_DISCHARGE);

	return 0;
}

int tcpci_report_power_control(struct sgm41620_tcpc *tcpc, bool en)
{
	if (tcpc->typec_power_ctrl == en)
		return 0;

	tcpc->typec_power_ctrl = en;

	if (en)
		tcpci_report_power_control_on(tcpc);
	else
		tcpci_report_power_control_off(tcpc);

	return 0;
}
